#pragma once
#include <vector>
#include <unordered_map>
#include <cmath>
#include <list>
#include "constants.h"

using namespace std;

class SiteInfo
{
public:
    vector<string> site_names;                          // 所有边缘节点名字
    vector<int> site_bw_upper;                          // 所有边缘节点的带宽上限
    unordered_map<string, int> site_name_to_id;         // 用于做site的映射 边缘节点名字->边缘节点在site_bandwidth的下标
    vector<vector<int>> site_to_clients;                // 用来存储每个site连接哪些client
    vector<int> site_connect_num;                       // 用来指示每个site连接的client的数量
    int N;                                              // 站点的数量
    bool site_reachable_matrix[MAX_N][MAX_M] = {false}; // 边缘节点与客户端的可达矩阵

    void add_site(string site_name, int bw_upper);

    void add_connect(int site_id, int client_id);
};

class ClientInfo
{
public:
    vector<string> client_names;                  // 用来存每个客户端的名字
    unordered_map<string, int> client_name_to_id; // 客户端名字到id的映射
    vector<vector<int>> client_to_sites;          // 用来存每个客户端连接哪些边缘节点
    vector<int> client_connect_num;               // 用来指示每个客户端连接的边缘节点的数量
    int M;                                        // 客户端数量

    void add_client(string client_name);
    void add_connect(int client_id, int site_id);
};

class ConfigInfo
{
public:
    int qos_constraint;
    int base_cost;
    double center_cost;
    int quantile_index;

    void set_quantile_index(int T);
};

class BandwidthNode
{
public:
    int stream_id;
    int client_id;
    int bandwidth;
    BandwidthNode(int stream_id, int client_id, int bandwidth);
};

class AlloNode
{
public:
    int stream_id;
    int client_id;
    int bandwidth;
    int site_id = -1;
    AlloNode *next_site_allo = NULL;
    AlloNode *pre_site_allo = NULL;
};

class TDemand // t时刻的需求信息
{
public:
    vector<string> stream_name_vec;                        // 数据流的名字
    unordered_map<string, int> stream_name_to_ids;         // 数据流名字到id的映射
    int demand_bw_matrix[MAX_P][MAX_M];                    // 数据流带宽请求矩阵
    vector<BandwidthNode> sorted_deamnd_bw;                // 数据流带宽请求列表，按照带宽请求量从大到小排序
    vector<vector<BandwidthNode>> sorted_stream_deamnd_bw; // 每个流按照带宽请求量从大到小排序
    int P;                                                 // 数据流的个数
    int t;                                                 // 时刻t

    TDemand(int t);

    void add_stream(string stream_name);
    void add_demand(const int &stream_id, const int &client_id, const int &demand_bw);
};

class DemandInfo
{
public:
    vector<TDemand *> demand_vec; // 记录每个时刻的需求
    int T;                        // 时间时刻的总数

    void add_demand(TDemand *tdemand);
};

class SiteAlloInfo
{
public:
    AlloNode *head = NULL;
    AlloNode *tail = NULL;
    int bw_sum = 0;
    int count = 0;
    int largest_bw = 0;
    void add_allocate(AlloNode *allo_node);
    void drop_allocate(AlloNode *allo_node);
};

class SiteAlloSortIter
{
public:
    list<AlloNode *> point_list;
    bool asc;

    SiteAlloSortIter(SiteAlloInfo (&stream_allo_list)[MAX_P], bool asc);
    void next(AlloNode *&allo_node);
};

class AssignOpt
{
public:
    int opt_type;
    int stream_id;
    int client_id;
    int site_id;
    AssignOpt(int opt_type, int stream_id, int client_id, int site_id);
};
class TAssign // 记录t时刻带宽分配方案
{
public:
    AlloNode stream_client_to_allo_node[MAX_P][MAX_M];   // 用来指示当前时刻每一个流的带宽分配给哪个边缘节点  [流id][客户端id]=边缘节点id
    SiteAlloInfo site_stream_to_allo_info[MAX_N][MAX_P]; // 用来记录每个边缘节点在每个流下分配到了哪些带宽请求
    TDemand *tdemand;                                    // 记录当前时刻的带宽请求信息
    int site_allocated_bw[MAX_N] = {0};                  // 用来记录当前时刻每一个边缘节点的带宽分配量
    int site_inherited_bw[MAX_N] = {0};                  // 用来记录边缘节点从上一时刻继承的流量
    int site_total_bw[MAX_N] = {0};                      // 记录边缘节点当前时刻带宽分配总量和上一时刻继承的带宽的和
    TAssign *pre_tassign;                                // 上一时刻的分配方案指针
    TAssign *next_tassign;                               // 下一时刻的分配方案指针
    long long center_site_bw = 0;                        // 中心节点的带宽大小
    bool record_snapshot = false;
    list<AssignOpt> *snapshot_opt_history;

    int t;
    int P;
    TAssign(TDemand *tdemand);
    void add_allocate(int stream_id, int client_id, int site_id);
    void drop_allocate(int stream_id, int client_id);
    void transfer(int stream_id, int client_id, int to_site_id);
    void refresh_inherited_bw();
    void refresh_total_bw();
    void snapshot();
    void restore();
    void cancel_snapshot();
};

class AssignInfo
{
public:
    vector<TAssign *> assign_vec;
    int score;

    AssignInfo(DemandInfo &demand_info);
};
